package com.zkt.practice.server.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.zkt.practice.api.enums.AnswerStatusEnum;
import com.zkt.practice.api.enums.CompleteStatusEnum;
import com.zkt.practice.api.enums.IsDeletedFlagEnum;
import com.zkt.practice.api.enums.SubjectInfoTypeEnum;
import com.zkt.practice.api.req.*;
import com.zkt.practice.api.vo.*;
import com.zkt.practice.server.entity.dto.SubjectDTO;
import com.zkt.practice.server.entity.dto.SubjectDetailDTO;
import com.zkt.practice.server.entity.dto.SubjectOptionDTO;
import com.zkt.practice.server.entity.dto.UserInfo;
import com.zkt.practice.server.entity.po.*;
import com.zkt.practice.server.mapper.*;
import com.zkt.practice.server.rpc.UserRpc;
import com.zkt.practice.server.service.PracticeDetailService;
import com.zkt.practice.server.utils.DateUtils;
import com.zkt.practice.server.utils.LoginUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 赵开泰
 * @program jc-club
 * @date 2025/3/23
 * @description 练题情况service层实现类
 **/

@Service
@Slf4j
public class PracticeDetailServiceImpl implements PracticeDetailService {
	
	@Resource
	private PracticeDetailMapper practiceDetailMapper;
	
	@Resource
	private PracticeSetMapper practiceSetMapper;
	
	@Resource
	private PracticeSetDetailMapper practiceSetDetailMapper;
	
	@Resource
	private PracticeMapper practiceMapper;
	
	@Resource
	private SubjectMapper subjectMapper;
	
	@Resource
	private SubjectRadioMapper subjectRadioMapper;
	
	@Resource
	private SubjectMultipleMapper subjectMultipleMapper;
	
	@Resource
	private SubjectJudgeMapper subjectJudgeMapper;
	
	@Resource
	private SubjectMappingMapper subjectMappingMapper;
	
	@Resource
	private SubjectLabelMapper subjectLabelMapper;
	
	@Resource
	private UserRpc userRpc;
	
	@Override
	@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
	public Boolean submit(SubmitPracticeDetailReq req) {
		PracticePO practicePO = new PracticePO();
		Long practiceId = req.getPracticeId();
		Long setId = req.getSetId();
		practicePO.setSetId(setId);
		practicePO.setTimeUse(req.getTimeUse());
		practicePO.setSubmitTime(DateUtils.parseStrToDate(req.getSubmitTime()));
		practicePO.setCompleteStatus(CompleteStatusEnum.COMPLETE.getCode());
		practicePO.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
		practicePO.setCreatedBy(LoginUtil.getLoginId());
		practicePO.setCreatedTime(LocalDateTime.now());
		// 计算正确率
		int correctCount = practiceDetailMapper.selectCorrectCount(practiceId);
		List<PracticeSetDetailPO> practiceSetDetailPOS = practiceSetDetailMapper.selectBySetId(setId);
		int totalCount = practiceSetDetailPOS.size();
		BigDecimal correctRate = new BigDecimal(correctCount)
				.divide(new BigDecimal(totalCount), 4, BigDecimal.ROUND_HALF_UP)
				.multiply(new BigDecimal("100.00"));
		practicePO.setCorrectRate(correctRate);
		PracticePO po = practiceMapper.selectById(practiceId);
		if (Objects.isNull(po)) {
			practiceMapper.insert(practicePO);
		} else {
			practicePO.setId(practiceId);
			practiceMapper.update(practicePO);
		}
		practiceSetMapper.updateHeat(setId);
		// 补充剩余题目的记录
		List<PracticeDetailPO> practiceDetailPOList = practiceDetailMapper.selectByPracticeId(practiceId);
		List<PracticeSetDetailPO> minusList = practiceSetDetailPOS.stream()
				.filter(item -> !practiceDetailPOList.stream()
						.map(e -> e.getSubjectId())
						.collect(Collectors.toList())
						.contains(item.getSubjectId()))
				.collect(Collectors.toList());
		if (log.isInfoEnabled()) {
			log.info("题目差集{}", JSON.toJSONString(minusList));
		}
		if (CollUtil.isNotEmpty(minusList)) {
			minusList.forEach(e -> {
				PracticeDetailPO practiceDetailPO = new PracticeDetailPO();
				practiceDetailPO.setPracticeId(practiceId);
				practiceDetailPO.setSubjectType(e.getSubjectType());
				practiceDetailPO.setSubjectId(e.getSubjectId());
				practiceDetailPO.setAnswerStatus(0);
				practiceDetailPO.setAnswerContent("");
				practiceDetailPO.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
				practiceDetailPO.setCreatedTime(LocalDateTime.now());
				practiceDetailPO.setCreatedBy(LoginUtil.getLoginId());
				practiceDetailMapper.insertSingle(practiceDetailPO);
			});
		}
		return true;
	}
	
	@Override
	@Transactional(rollbackFor = Exception.class)
	public Boolean submitSubject(SubmitSubjectDetailReq req) {
		PracticePO practicePO = new PracticePO();
		practicePO.setId(req.getPracticeId());
		practicePO.setTimeUse(req.getTimeUse());
		practicePO.setSubmitTime(LocalDateTime.now());
		practiceMapper.update(practicePO);
		
		PracticeDetailPO practiceDetailPO = new PracticeDetailPO();
		practiceDetailPO.setPracticeId(req.getPracticeId());
		practiceDetailPO.setSubjectId(req.getSubjectId());
		practiceDetailPO.setSubjectType(req.getSubjectType());
		String answerContent = "";
		// 排序答案
		if (CollUtil.isNotEmpty(req.getAnswerContents())) {
			List<Integer> answerContents = req.getAnswerContents();
			Collections.sort(answerContents);
			answerContent = StrUtil.join(",", answerContents);
		}
		practiceDetailPO.setAnswerContent(answerContent);
		SubjectDTO subjectDTO = new SubjectDTO();
		subjectDTO.setSubjectId(req.getSubjectId());
		subjectDTO.setSubjectType(req.getSubjectType());
		// 获取正确答案，并判断答案是否正确
		SubjectDetailDTO subjectDetail = getSubjectDetail(subjectDTO);
		StringBuffer correctAnswer = new StringBuffer();
		if (req.getSubjectType().equals(SubjectInfoTypeEnum.JUDGE.getCode())) {
			Integer isCorrect = subjectDetail.getIsCorrect();
			correctAnswer.append(isCorrect);
		} else {
			subjectDetail.getOptionList().forEach(e -> {
				if (Objects.equals(e.getIsCorrect(), 1)) {
					correctAnswer.append(e.getOptionType()).append(",");
				}
			});
			if (correctAnswer.length() > 0) {
				correctAnswer.deleteCharAt(correctAnswer.length() - 1);
			}
		}
		if (Objects.equals(correctAnswer.toString(), answerContent)) {
			practiceDetailPO.setAnswerStatus(1);
		} else {
			practiceDetailPO.setAnswerStatus(0);
		}
		practiceDetailPO.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
		practiceDetailPO.setCreatedBy(LoginUtil.getLoginId());
		practiceDetailPO.setCreatedTime(LocalDateTime.now());
		PracticeDetailPO existDetail = practiceDetailMapper.selectDetail(req.getPracticeId(), req.getSubjectId(), LoginUtil.getLoginId());
		if (Objects.isNull(existDetail)) {
			practiceDetailMapper.insertSingle(practiceDetailPO);
		} else {
			practiceDetailPO.setId(existDetail.getId());
			practiceDetailMapper.update(practiceDetailPO);
		}
		return true;
	}
	
	
	public SubjectDetailDTO getSubjectDetail(SubjectDTO dto) {
		SubjectDetailDTO subjectDetailDTO = new SubjectDetailDTO();
		SubjectPO subjectPO = subjectMapper.selectById(dto.getSubjectId());
		if (dto.getSubjectType() == SubjectInfoTypeEnum.RADIO.getCode()) {
			List<SubjectOptionDTO> optionList = new LinkedList<>();
			List<SubjectRadioPO> radioSubjectPOS = subjectRadioMapper.selectBySubjectId(subjectPO.getId());
			radioSubjectPOS.forEach(e -> {
				SubjectOptionDTO subjectOptionDTO = new SubjectOptionDTO();
				subjectOptionDTO.setOptionContent(e.getOptionContent());
				subjectOptionDTO.setOptionType(e.getOptionType());
				subjectOptionDTO.setIsCorrect(e.getIsCorrect());
				optionList.add(subjectOptionDTO);
			});
			subjectDetailDTO.setOptionList(optionList);
		}
		if (dto.getSubjectType() == SubjectInfoTypeEnum.MULTIPLE.getCode()) {
			List<SubjectOptionDTO> optionList = new LinkedList<>();
			List<SubjectMultiplePO> multipleSubjectPOS = subjectMultipleMapper.selectBySubjectId(subjectPO.getId());
			multipleSubjectPOS.forEach(e -> {
				SubjectOptionDTO subjectOptionDTO = new SubjectOptionDTO();
				subjectOptionDTO.setOptionContent(e.getOptionContent());
				subjectOptionDTO.setOptionType(e.getOptionType());
				subjectOptionDTO.setIsCorrect(e.getIsCorrect());
				optionList.add(subjectOptionDTO);
			});
			subjectDetailDTO.setOptionList(optionList);
		}
		if (dto.getSubjectType() == SubjectInfoTypeEnum.JUDGE.getCode()) {
			SubjectJudgePO judgeSubjectPO = subjectJudgeMapper.selectBySubjectId(subjectPO.getId());
			subjectDetailDTO.setIsCorrect(judgeSubjectPO.getIsCorrect());
		}
		subjectDetailDTO.setSubjectParse(subjectPO.getSubjectParse());
		subjectDetailDTO.setSubjectName(subjectPO.getSubjectName());
		return subjectDetailDTO;
	}
	
	@Override
	public List<ScoreDetailVO> getScoreDetail(GetScoreDetailReq req) {
		Long practiceId = req.getPracticeId();
		List<ScoreDetailVO> list = new LinkedList<>();
		List<PracticeDetailPO> practiceDetailPOList = practiceDetailMapper.selectByPracticeId(practiceId);
		if (CollUtil.isEmpty(practiceDetailPOList)) {
			return Collections.emptyList();
		}
		practiceDetailPOList.forEach(po -> {
			ScoreDetailVO scoreDetailVO = new ScoreDetailVO();
			scoreDetailVO.setSubjectId(po.getSubjectId());
			scoreDetailVO.setSubjectType(po.getSubjectType());
			scoreDetailVO.setIsCorrect(po.getAnswerStatus());
			list.add(scoreDetailVO);
		});
		return list;
	}
	
	@Override
	public SubjectDetailVO getSubjectDetail(GetSubjectDetailReq req) {
		SubjectDetailVO subjectDetailVO = new SubjectDetailVO();
		Long subjectId = req.getSubjectId();
		Integer subjectType = req.getSubjectType();
		SubjectDTO subjectDTO = new SubjectDTO();
		subjectDTO.setSubjectId(subjectId);
		subjectDTO.setSubjectType(subjectType);
		SubjectDetailDTO subjectDetail = getSubjectDetail(subjectDTO);
		List<SubjectOptionDTO> optionList = subjectDetail.getOptionList();
		List<PracticeSubjectOptionVO> optionVOList = new LinkedList<>();
		List<Integer> correctAnswer = new LinkedList<>();
		if (CollUtil.isNotEmpty(optionList)) {
			optionList.forEach(option -> {
				PracticeSubjectOptionVO optionVO = new PracticeSubjectOptionVO();
				optionVO.setOptionType(option.getOptionType());
				optionVO.setOptionContent(option.getOptionContent());
				optionVO.setIsCorrect(option.getIsCorrect());
				optionVOList.add(optionVO);
				if (option.getIsCorrect() == 1) {
					correctAnswer.add(option.getOptionType());
				}
			});
		}
		if (subjectType.equals(SubjectInfoTypeEnum.JUDGE.getCode())) {
			Integer isCorrect = subjectDetail.getIsCorrect();
			PracticeSubjectOptionVO correctOption = new PracticeSubjectOptionVO();
			correctOption.setOptionType(1);
			correctOption.setOptionContent("正确");
			correctOption.setIsCorrect(isCorrect == 1 ? 1 : 0);
			PracticeSubjectOptionVO errorOptionVO = new PracticeSubjectOptionVO();
			errorOptionVO.setOptionType(2);
			errorOptionVO.setOptionContent("错误");
			errorOptionVO.setIsCorrect(isCorrect == 0 ? 1 : 0);
			optionVOList.add(correctOption);
			optionVOList.add(errorOptionVO);
			correctAnswer.add(subjectDetail.getIsCorrect());
		}
		subjectDetailVO.setOptionList(optionVOList);
		subjectDetailVO.setSubjectParse(subjectDetail.getSubjectParse());
		subjectDetailVO.setSubjectName(subjectDetail.getSubjectName());
		subjectDetailVO.setCorrectAnswer(correctAnswer);
		// 自己的答题答案
		List<Integer> respondAnswer = new LinkedList<>();
		PracticeDetailPO practiceDetailPO = practiceDetailMapper.selectAnswer(req.getPracticeId(), subjectId);
		String answerContent = practiceDetailPO.getAnswerContent();
		if (StrUtil.isNotBlank(answerContent)) {
			String[] split = answerContent.split(",");
			for (String s : split) {
				respondAnswer.add(Integer.valueOf(s));
			}
		}
		subjectDetailVO.setRespondAnswer(respondAnswer);
		List<SubjectMappingPO> subjectMappingPOList = subjectMappingMapper.getLabelIdsBySubjectId(subjectId);
		List<Long> labelIdList = new LinkedList<>();
		subjectMappingPOList.forEach(subjectMappingPO -> {
			labelIdList.add(subjectMappingPO.getLabelId());
		});
		List<String> labelNameList = subjectLabelMapper.getLabelNameByIds(labelIdList);
		subjectDetailVO.setLabelNames(labelNameList);
		return subjectDetailVO;
	}
	
	@Override
	public ReportVO getReport(GetReportReq req) {
		ReportVO reportVO = new ReportVO();
		Long practiceId = req.getPracticeId();
		PracticePO practicePO = practiceMapper.selectById(practiceId);
		Long setId = practicePO.getSetId();
		PracticeSetPO practiceSetPO = practiceSetMapper.selectById(setId);
		reportVO.setTitle(practiceSetPO.getSetName());
		List<PracticeDetailPO> practiceDetailPOList = practiceDetailMapper.selectByPracticeId(practiceId);
		if (CollUtil.isEmpty(practiceDetailPOList)) {
			return null;
		}
		int totalCount = practiceDetailPOList.size();
		List<PracticeDetailPO> correctPoList = practiceDetailPOList.stream().filter(e ->
				Objects.equals(e.getAnswerStatus(), AnswerStatusEnum.CORRECT.getCode())).collect(Collectors.toList());
		reportVO.setCorrectSubject(correctPoList.size() + "/" + totalCount);
		List<ReportSkillVO> reportSkillVOS = new LinkedList<>();
		Map<Long, Integer> totalMap = getSubjectLabelMap(practiceDetailPOList);
		Map<Long, Integer> correctMap = getSubjectLabelMap(correctPoList);
		totalMap.forEach((key, val) -> {
			ReportSkillVO skillVO = new ReportSkillVO();
			SubjectLabelPO labelPO = subjectLabelMapper.queryById(key);
			String labelName = labelPO.getLabelName();
			Integer correctCount = correctMap.get(key);
			if (Objects.isNull(correctCount)) {
				correctCount = 0;
			}
			skillVO.setName(labelName);
			BigDecimal rate = BigDecimal.ZERO;
			if (!Objects.equals(val, 0)) {
				rate = new BigDecimal(correctCount.toString()).divide(new BigDecimal(val.toString()), 4,
						BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100));
			}
			skillVO.setStar(rate);
			reportSkillVOS.add(skillVO);
		});
		if (log.isInfoEnabled()) {
			log.info("获取到的正确率{}", JSON.toJSONString(reportSkillVOS));
		}
		reportVO.setSkill(reportSkillVOS);
		return reportVO;
	}
	
	
	private Map<Long, Integer> getSubjectLabelMap(List<PracticeDetailPO> practiceDetailPOList) {
		if (CollUtil.isEmpty(practiceDetailPOList)) {
			return Collections.emptyMap();
		}
		Map<Long, Integer> map = new HashMap<>();
		practiceDetailPOList.forEach(detail -> {
			Long subjectId = detail.getSubjectId();
			List<SubjectMappingPO> labelIdPO = subjectMappingMapper.getLabelIdsBySubjectId(subjectId);
			labelIdPO.forEach(po -> {
				Long labelId = po.getLabelId();
				if (Objects.isNull(map.get(labelId))) {
					map.put(labelId, 1);
					return;
				}
				map.put(labelId, map.get(labelId) + 1);
			});
		});
		if (log.isInfoEnabled()) {
			log.info("获取到的题目对应的标签map{}", JSON.toJSONString(map));
		}
		return map;
	}
	
	@Override
	public List<RankVO> getPracticeRankList() {
		List<RankVO> list = new LinkedList<>();
		List<PracticeRankPO> poList = practiceDetailMapper.getPracticeCount();
		if (CollUtil.isEmpty(poList)) {
			return list;
		}
		poList.forEach(e -> {
			RankVO rankVO = new RankVO();
			rankVO.setCount(e.getCount());
			UserInfo userInfo = userRpc.getUserInfo(e.getCreatedBy());
			rankVO.setName(userInfo.getNickName());
			rankVO.setAvatar(userInfo.getAvatar());
			list.add(rankVO);
		});
		return list;
	}
	
	@Override
	@Transactional(rollbackFor = Exception.class)
	public Boolean giveUp(Long practiceId) {
		practiceDetailMapper.deleteByPracticeId(practiceId);
		practiceMapper.deleteById(practiceId);
		return true;
	}
	
}
